/*
   Pass parameters to thread functions
*/


#include <thread>
#include <process.h>
#define _beginthreadex std::thread 

/*
    传递参数给std::thread构造函数
    参数会以默认的方式被复制到内部存储空间，在那里新创建的执行线程可以访问它们
    即便函数中的相应参数期待着引用
*/
void f(int i, std::string const& s);
std::thread t(f, 3, "hello");

/*
    传递参数给std::thread构造函数
    同时因为字符串在实际运行中被解释为char* 传送，再被转换为std::string
    所以存在未定义的行为（所有提供的参数是一个自动变量的指针时，均会出现）
*/
void oops(int some_param) {
   char buffer[1024];
   sprintf(buffer, "%i", some_param);
   std::thread t(f, 3, buffer);     // 传递的buffer的指针
   t.detach();
} // buffer 变量被释放，但此时缓冲可能额还未在新线程t上被转换为std::string
// 解决方法
void not_oops(int some_param) {
   char buffer[1024];
   sprintf(buffer, "%i", some_param);
   std::thread t(f, 3, std::string(buffer));    // 使用std::string避免悬浮指针
   t.detach();
}
/***
 * 上方法，依赖从缓冲的指针到函数所期望的std::string对象的隐式转换
 * std::thread构造函数鸳鸯复制了所提供的值，并未转换为期望的参数类型
 * 所以存在以下情况，对象被复制，但期望得到的是引用
 * （发生再线程正在更新一个通过引用传递来的数据结构时）
***/
void update_data_for_widget(widget_id w, widget_data& data);   // 一个期望第二个参数为引用的函数
void oops_again(widget_id w) {
   widget_data data;
   std:thread t(update_data_for_widget, w, data);     // 线程中处理的为data的副本   tips1
   display_status();
   t.join();
   process_widget_data(data);    // data无变化
}/*** 解决方法 std::bind() 中类似的解决
 * tips1改为：std::thread t(update_data_for_widget, w, std::ref(data));
 * 详细：https://blog.csdn.net/Destiny_zc/article/details/118959979
 * 总结：std::bind() 目的是解决函数中需要传入过多参数，且大部分参数在多数情况均是默认值的情况
 *    （另一种复杂是在接口函数外再封一层函数,将不变的参数固定，调用最外部函数即可）
 *    std::bind()函数中的参数在被复制或者移动时绝不会以引用的方式传递，除非你使用了std::ref()或者std::cref()包装的参数
***/

class X {
   public:
      void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_lengthy_work, &my_x);   // 参数传递语义
// std::thread 实例是可移动的，不可复制的。 类似std::unique_ptr相同的所有权语义